#include <stx/imp/cmn.hpp>

inline namespace stx
{
bool CiLess::operator()(std::string_view const sv1,
                        std::string_view const sv2) const
{
    return ranges::lexicographical_compare(
        sv1,
        sv2,
        [](auto const c1, auto const c2)
        { return std::tolower(c1) < std::tolower(c2); });
}

bool CiEqualTo::operator()(std::string_view const sv1,
                           std::string_view const sv2) const
{
    return iCaseEqual(sv1, sv2);
}

Lines::Lines(fs::path const& fp)
{
    auto css_ctx  = CharStreamSrcContext_{};
    css_ctx.up_is = std::make_unique<std::ifstream>(fp, std::ios::binary);
    this->src_    = std::move(css_ctx);
}

Lines::Lines(std::string_view const sv_src)
    : src_(StringViewSrcContext_{.sv_src = sv_src})
{}

Lines::iterator::iterator(Lines* p_owner) noexcept : p_owner_(p_owner)
{}

std::string_view Lines::iterator::operator*() const
{
    return this->p_owner_->sv_cur_;
}

std::string_view* Lines::iterator::operator->() const
{
    return &this->p_owner_->sv_cur_;
}

Lines::iterator& Lines::iterator::operator++()
{
    if (!this->p_owner_)
    {
        return *this;
    }

    if (!this->p_owner_->moveToNext_())
    {
        this->p_owner_ = {};
    }

    return *this;
}

Lines::iterator Lines::begin() noexcept
{
    if (this->moveToNext_())
    {
        return iterator(this);
    }

    return {};
}

Lines::iterator Lines::end() noexcept
{
    return {};
}

bool Lines::moveToNext_()
{
    if (std::holds_alternative<StringViewSrcContext_>(this->src_))
    {
        auto& sv_ctx = std::get<StringViewSrcContext_>(this->src_);
        if (sv_ctx.cur_src_pos >= sv_ctx.sv_src.size())
        {
            return false;
        }

        for (auto pos = sv_ctx.cur_src_pos; pos < sv_ctx.sv_src.size(); ++pos)
        {
            if ('\n' == sv_ctx.sv_src[pos])
            {
                this->sv_cur_ = tosv(sv_ctx.sv_src.data() + sv_ctx.cur_src_pos,
                                     pos - sv_ctx.cur_src_pos);
                sv_ctx.cur_src_pos = pos + 1;
                return true;
            }
        }

        this->sv_cur_      = tosv(sv_ctx.sv_src.data() + sv_ctx.cur_src_pos,
                             sv_ctx.sv_src.size() - sv_ctx.cur_src_pos);
        sv_ctx.cur_src_pos = sv_ctx.sv_src.size();
        return true;
    }

    if (std::holds_alternative<CharStreamSrcContext_>(this->src_))
    {
        auto& cs_ctx = std::get<CharStreamSrcContext_>(this->src_);
        if (cs_ctx.up_is && std::getline(*cs_ctx.up_is, cs_ctx.s_cur))
        {
            this->sv_cur_ = cs_ctx.s_cur;
            return true;
        }
    }

    return false;
}
} // namespace stx